package com.icode.core.shared.usertype;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.usertype.ParameterizedType;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;

/**
 * User: alexz
 * Date: 15-1-20
 * Time: 下午4:40
 * <p/>
 * map several string values into single column
 */
public class StringsUserType extends AbstractHibernateUserType implements ParameterizedType {
    private String delimiter;
    private Class collectionType;
    private Class elementType;

    @Override
    public int[] sqlTypes() {
        return new int[]{Types.VARCHAR};
    }

    @Override
    public Class returnedClass() {
        return collectionType;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
        String value = (String) StandardBasicTypes.STRING.nullSafeGet(rs, names, session, owner);
        if (value == null) {
            return Collections.emptySet();
        }
        String[] values = value.split(delimiter);
        try {
            Collection results = (Collection) collectionType.newInstance();
            for (String s : values) {
                try {
                    Constructor constructor = elementType.getDeclaredConstructor(String.class);
                    Object o = constructor.newInstance(s);
                    results.add(o);
                } catch (NoSuchMethodException e) {
                    throw new IllegalStateException("no constructor includes one string parameter exists for the class " + collectionType);
                } catch (InvocationTargetException e) {
                    throw new IllegalStateException("can not use the constructor includes one string parameter for the class" + collectionType);
                }
            }
            return results;
        } catch (InstantiationException e) {
            throw new IllegalStateException("can not make a new instance for the class " + collectionType);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("can not make a new instance for the class " + collectionType);
        }
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            StandardBasicTypes.STRING.nullSafeSet(st, null, index, session);
        } else {
            Collection values = (Collection) value;
            if (values.size() > 0) {
                String valueString = "";
                for (Object s : values) {
                    valueString += s.toString() + delimiter;
                }
                valueString = valueString.substring(0, valueString.lastIndexOf(delimiter));
                StandardBasicTypes.STRING.nullSafeSet(st, valueString, index, session);
            } else {
                StandardBasicTypes.STRING.nullSafeSet(st, null, index, session);
            }
        }
    }

    @Override
    public void setParameterValues(Properties parameters) {
        this.delimiter = parameters.getProperty("delimiter", ",");

        String collectionType = parameters.getProperty("collectionType", "java.util.HashSet");
        try {
            this.collectionType = Class.forName(collectionType);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("no class " + collectionType + " found.");
        }

        String elementType = parameters.getProperty("elementType", "java.lang.String");
        try {
            this.elementType = Class.forName(elementType);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("no class " + elementType + " found.");
        }
    }
}
